package JavaLab.Collections;

import java.util.Comparator;
import java.util.Iterator;

public class MyTreeMap implements MyMap{
    private RedBlackTree tree;

    public MyTreeMap(){
        tree = new RedBlackTree();
    }
    public MyTreeMap(Comparator comparator){
        tree = new RedBlackTree(comparator);
    }

    public void clear() {
        while(tree.root != null){
            tree.removeNode(tree.root);
        }
    }

    public boolean containsKey(final Object key) {

        class KeyOccurrenceFinder implements RedBlackTree.Visitor{
            boolean result;

            public void visit(RedBlackTree.Node n) {
                if(n.data instanceof SimpleEntry &&
                        ((SimpleEntry)n.data).key.equals(key)){
                    result = true;
                }
            }
        }

        KeyOccurrenceFinder keyFinder = new KeyOccurrenceFinder();
        tree.inOrderVisit(keyFinder);
        return keyFinder.result;
    }

    public boolean containsValue(final Object value) {

        class ValueOccurrenceFinder implements RedBlackTree.Visitor{
            boolean result;

            public void visit(RedBlackTree.Node n) {
                if(n.data instanceof SimpleEntry &&
                        ((SimpleEntry)n.data).value.equals(value)){
                    result = true;
                }
            }
        }

        ValueOccurrenceFinder valueOccurrenceFinder = new ValueOccurrenceFinder();
        tree.inOrderVisit(valueOccurrenceFinder);
        return valueOccurrenceFinder.result;
    }

    public Object get(final Object key) {
        if(!(key instanceof Comparable)){
            return null;
        }
        ByKeyFinder byKeyFinder = new ByKeyFinder((Comparable)key);
        tree.inOrderVisit(byKeyFinder);
        return (byKeyFinder.result == null) ? null : byKeyFinder.result.value;
    }

    public boolean isEmpty() {
        return tree.root == null;
    }

    public Object put(Object key, Object value) {
        if(!(key instanceof  Comparable)){
            return null;
        }
        SimpleEntry newEntry = new SimpleEntry((Comparable)key, value);
        tree.add(newEntry);
        return newEntry;
    }

    public Object remove(Object key) {
        if(!(key instanceof Comparable))
            return null;
        ByKeyFinder byKeyFinder = new ByKeyFinder((Comparable)key);
        tree.inOrderVisit(byKeyFinder);
        tree.remove(byKeyFinder.result);
        return byKeyFinder.result;
    }

    public int size() {

        class Counter implements RedBlackTree.Visitor{
            int result = 0;

            public void visit(RedBlackTree.Node n) {
                result++;
            }
        }

        Counter counter = new Counter();
        tree.inOrderVisit(counter);
        return counter.result;
    }

    public Iterator entryIterator() {
        return entryIterator();
    }

    private class ByKeyFinder implements RedBlackTree.Visitor{
        SimpleEntry result;
        Comparable key;

        ByKeyFinder(Comparable key){
            this.key = key;
        }

        public void visit(RedBlackTree.Node n) {
            if(n.data instanceof SimpleEntry &&
                    ((SimpleEntry)n.data).key.equals(key)){
                result = (result == null) ? ((SimpleEntry)n.data) : result;
            }
        }
    }

    public class SimpleEntry implements MyMap.Entry, Comparable<SimpleEntry>{
        final Comparable key;
        Object value;

        public SimpleEntry(Comparable key, Object value){
            this.key = key;
            this.value = value;
        }

        public Object getKey() {
            return key;
        }

        public Object getValue() {
            return value;
        }

        public Object setValue(Object value) {
            Object oldValue = value;
            this.value = value;
            return  oldValue;
        }

        public int compareTo(SimpleEntry o) {
            return key.compareTo(o.key);
        }
    }

    private class EntryIterator implements Iterator{
        SimpleEntry next;
        int index;
        SimpleEntry current;
        SimpleEntry[] simpleEntriesList;

        EntryIterator(){
            TreeToList treeToList = new TreeToList();
            tree.inOrderVisit(treeToList);
            simpleEntriesList = treeToList.simpleEntriesList;

            index = 0;
            current = simpleEntriesList[index];
            next = simpleEntriesList[index + 1];
        }

        private class TreeToList implements RedBlackTree.Visitor{
            SimpleEntry[] simpleEntriesList;
            int counter;

            TreeToList(){
                simpleEntriesList = new SimpleEntry[size()];
                counter = 0;
            }

            public void visit(RedBlackTree.Node n) {
                simpleEntriesList[counter++] = ((SimpleEntry)n.data);
            }
        }

        public boolean hasNext() {
            return next != null;
        }

        public Object next() {
            SimpleEntry result = current;
            current = simpleEntriesList[++index];
            next = simpleEntriesList[index + 1];
            return result;
        }

        public void remove() {
            tree.remove(current);
        }
    }
}